OpenCV + Pythonでの図形検出、図形数える

前回の記事は「OpenCV + Pythonでの直線検出」を説明しました。今回はOpenCV + Pythonで図形を検出のことを説明します。

OpenCVのライブラリは領域(輪郭)の特徴のAPIがあります。領域(輪郭)の特徴である面積,周囲長,重心,外接矩形などの値を検出できます。

今回は面積(Contour area)、周囲長(arc length)、輪郭の近似(approxPolyDP)の関数で図形検出し、図形の数を数えます。

画像のオレンジの円形を検出します。

In [1]:
import cv2
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
%matplotlib inline
In [2]:
img = cv2.imread("orange.png")

plt.figure(figsize=(10, 10))
img2 = img[:,:,::-1]
plt.xticks([]), plt.yticks([]) 
plt.imshow(img2)
Out[2]:
<matplotlib.image.AxesImage at 0x1efb5bb46d8>

Pythonコード:

最初はファイルを読み込みで、グレースケール化します。そして、しきい値指定と輪郭を抽出します。最後は画像ファイルを作成します。

In [3]:
# ファイルを読み込み グレースケール化
img = cv2.imread("orange.png", cv2.IMREAD_GRAYSCALE)

# しきい値指定によるフィルタリング
_, threshold = cv2.threshold(img, 240, 255, cv2.THRESH_BINARY)

# 輪郭を抽出
_, contours, _ = cv2.findContours(threshold, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

font = cv2.FONT_HERSHEY_DUPLEX

for cnt in contours:
    approx = cv2.approxPolyDP(cnt, 0.01*cv2.arcLength(cnt, True), True)
    cv2.drawContours(img, [approx], 0, (0), 2)
    x = approx.ravel()[0]
    y = approx.ravel()[1]

    if len(approx) > 10:
        cv2.putText(img, "circle", (x, y), font, 1, (0))   

# 結果の画像作成
cv2.imwrite('output_circle.png',img)
Out[3]:
True

結果の画像はの円の図形を検出し、領域輪郭を作成しました。

In [4]:
img = cv2.imread("output_circle.png")

plt.figure(figsize=(10, 10))
img2 = img[:,:,::-1]
plt.xticks([]), plt.yticks([]) 
plt.imshow(img2)
Out[4]:
<matplotlib.image.AxesImage at 0x1efb5e76438>

図形を数える

上記のスクリプトを参照し、図形を数えるコードを追加します。(### のコメント)

図形の数の変数作成し、画像に数を入れます。

Pythonコード:

In [5]:
import cv2
import numpy as np

# ファイルを読み込み グレースケール化
img = cv2.imread("orange.png", cv2.IMREAD_GRAYSCALE)

# しきい値指定によるフィルタリング
_, threshold = cv2.threshold(img, 240, 255, cv2.THRESH_BINARY)

# 輪郭を抽出
_, contours, _ = cv2.findContours(threshold, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

font = cv2.FONT_HERSHEY_DUPLEX

### 図形の数の変数
circle = 0

for cnt in contours:
    approx = cv2.approxPolyDP(cnt, 0.01*cv2.arcLength(cnt, True), True)
    cv2.drawContours(img, [approx], 0, (0), 2)
    x = approx.ravel()[0]
    y = approx.ravel()[1]

    if len(approx) > 10:
        ### 図形を数える
        circle +=1
        cv2.putText(img, "circle{}".format(circle), (x, y), font, 0.8, (0))      

# 結果の画像作成
cv2.imwrite('output_count.png',img)
        
### 図形の数の結果
print('Number of circle = ' , circle)
Number of circle =  13

結果の画像

各図形を検出し、下から数えました。

In [6]:
img = cv2.imread("output_count.png")

plt.figure(figsize=(10, 10))
img2 = img[:,:,::-1]
plt.xticks([]), plt.yticks([]) 
plt.imshow(img2)
Out[6]:
<matplotlib.image.AxesImage at 0x1efb5fab6d8>

多数な図形を数える

下の画像から、円、楕円、三角形、四角形を数えます。図形検出のために、周囲長の数(v2.arcLength)です。

図形検出の設定

三角形 = 3、四角形 = 4、五角形 = 5、楕円形 > 6、<14、円形 > 16

In [7]:
img = cv2.imread("shapes.png")

plt.figure(figsize=(10, 10))
img2 = img[:,:,::-1]
plt.xticks([]), plt.yticks([]) 
plt.imshow(img2)
Out[7]:
<matplotlib.image.AxesImage at 0x1efb5fdd898>
In [8]:
import cv2
import numpy as np

# ファイルを読み込み グレースケール化
img = cv2.imread("shapes.png", cv2.IMREAD_GRAYSCALE)

# しきい値指定によるフィルタリング
_, threshold = cv2.threshold(img, 240, 255, cv2.THRESH_BINARY)

# 輪郭を抽出
_, contours, _ = cv2.findContours(threshold, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

font = cv2.FONT_HERSHEY_DUPLEX

# 図形の数の変数
triangle = 0
rectangle = 0
pentagon = 0
oval = 0
circle = 0

# 図形の設定
for cnt in contours:
    approx = cv2.approxPolyDP(cnt, 0.01*cv2.arcLength(cnt, True), True)
    cv2.drawContours(img, [approx], 0, (0), 2)
    x = approx.ravel()[0]
    y = approx.ravel()[1]

    if len(approx) == 3:
        triangle +=1
        cv2.putText(img, "triangle{}".format(triangle),  (x, y), font, 0.8, (0))
        
    elif len(approx) == 4:
        rectangle +=1
        cv2.putText(img, "rectangle{}".format(rectangle),  (x, y), font, 0.8, (0))
                
    elif len(approx) == 5:
        pentagon +=1
        cv2.putText(img, "pentagon{}".format(pentagon),  (x, y), font, 0.8, (0))
        
    elif 6 < len(approx) < 14:
        oval +=1
        cv2.putText(img, "oval{}".format(oval),  (x, y), font, 0.8, (0))
        
    else:
        circle +=1
        cv2.putText(img, "circle{}".format(circle), (x, y), font, 0.8, (0))
        
# 結果の画像作成
cv2.imwrite('output_shapes.png',img)

# 図形の数の結果
print('Number of triangle = ' , triangle)
print('Number of rectangle = ' , rectangle)
print('Number of pentagon = ' , pentagon)
print('Number of circle = ' , circle)
print('Number of oval = ' , oval)
Number of triangle =  3
Number of rectangle =  6
Number of pentagon =  0
Number of circle =  4
Number of oval =  2

結果

OpendCVのライブラリで面積、周囲長、輪郭の近似を計算し、各図形を数えることができました。

In [9]:
img = cv2.imread("output_shapes.png")
plt.figure(figsize=(10, 10))
img2 = img[:,:,::-1]
plt.xticks([]), plt.yticks([]) 
plt.imshow(img2)
Out[9]:
<matplotlib.image.AxesImage at 0x1efb601db38>